package com.wpam.scrabble.mobile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Timer;
import java.util.TimerTask;

import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Vibrator;
import android.util.Pair;

import com.wpam.scrabble.mobile.activities.GameResultsActivity;
import com.wpam.scrabble.mobile.activities.GameSettingsActivity;
import com.wpam.scrabble.mobile.communication.CommunicationManager;
import com.wpam.scrabble.mobile.communication.ToastRunnable;
import com.wpam.scrabble.mobile.messages.Message;
import com.wpam.scrabble.mobile.messages.MoveMessage;
import com.wpam.scrabble.mobile.messages.MsgSerializer;
import com.wpam.scrabble.mobile.messages.NewTurnMessage;
import com.wpam.scrabble.mobile.messages.StartMessage;
import com.wpam.scrabble.mobile.ui.GameBoard;
import com.wpam.scrabble.mobile.GameData;
import com.wpam.scrabble.mobile.activities.BoardActivity;;

public class GameLogic {

	public static final int RUNNING = 0;
	public static final int PLAYERS_DISCONNECTED = 1;
	public static final int PLAYERS_DONE = 2;
	public static final int I_AM_DONE = 3;
	public static final int SERVER_DISCONNECTED = 4;
	public static final int SERVER_ENDED = 5;
	
	private static GameLogic instance = null;
	private static long[] pattern = {0, 200, 200, 200};
	private boolean firstTurn_;
	private MoveInfo currentMove_;
	private MoveInfo previousMove_;
	private ScoreBoard scoreBoard_;
	private GameBoard gameBoard_;
	private String myName_;
	private Timer timer_;
	private StartMessage startMessage_;
	private GameData gameData_;
	private int gamePaused_;
	private LetterManager letterManager_;
	private PlayerManager playerManager_;
	private Vibrator vibrator_;
	
	private GameLogic() {
		
		firstTurn_ = true;
		currentMove_ = null;
		previousMove_ = null;
		scoreBoard_ = null;
		gameBoard_ = null;
		myName_ = null;
		timer_ = null;
		startMessage_ = null;
		gameData_ = null;
		gamePaused_ = 0;
		letterManager_ = null;
		playerManager_ = null;
		vibrator_ = null;
	}
	
	public static GameLogic getInstance() {
		
		if (instance == null)
			instance = new GameLogic();
		return instance;
	}
	
	public void setupGame(ArrayList<String> playerNames) {

		letterManager_ = new LetterManager(playerNames);
		firstTurn_ = true;
		startMessage_ = null;
		gamePaused_ = 0;
		currentMove_ = new MoveInfo();
		scoreBoard_ = new ScoreBoard(playerNames);
		myName_ = CommunicationManager.getMyName();
		timer_ = null;
		CommunicationManager.getInstance().sendStartMessageToEverybody(playerNames);
		gameBoard_.acceptNewLetters(letterManager_.getLettersOfPlayer(myName_));
		startTimer();
		gameBoard_.startMyTurn(isMyTurn());
		playerManager_ = new PlayerManager();
		vibrator_ = (Vibrator) gameBoard_.getContext().getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
	}
	
	public void setupGame() {

		letterManager_ = null;
		gamePaused_ = 0;
		currentMove_ = new MoveInfo();
		myName_ = CommunicationManager.getMyName();
		if (startMessage_ != null) {
			firstTurn_ = true;
			scoreBoard_ = new ScoreBoard(startMessage_.getPlayerNames());
			gameBoard_.acceptNewLetters(startMessage_.getUserLetters());
		} else if (gameData_ != null) {
			firstTurn_ = gameData_.getLetters().isEmpty();
			scoreBoard_ = gameData_.getScoreBoard();
			for (MovePart letter : gameData_.getLetters()) {
				gameBoard_.getSquare(letter.getRow(), letter.getCol()).setLetter(letter.getLetter());
				gameBoard_.getSquare(letter.getRow(), letter.getCol()).setAccepted();
			}
			gameBoard_.acceptNewLetters(gameData_.getUserLetters());
		}
		startMessage_ = null;
		gameData_ = null;
		timer_ = null;
		gameBoard_.startMyTurn(isMyTurn());
		vibrator_ = (Vibrator) gameBoard_.getContext().getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
	}
	
	public boolean isFirstTurn() {
		return firstTurn_;
	}
	
	public boolean isMyTurn() {
		return scoreBoard_.getCurrentPlayer().equals(myName_);
	}
	
	public MoveInfo getCurrentMove() {
		return currentMove_;
	}
	
	public ScoreBoard getScoreBoard() {
		return scoreBoard_;
	}

	public PlayerManager getPlayerManager() {
		return playerManager_;
	}
	
	public GameBoard getGameBoard() {
		return gameBoard_;
	}

	public void setGameBoard(GameBoard gameBoard) {
		gameBoard_ = gameBoard;
	}
	
	public boolean isGameRunning() {
		return myName_ != null;
	}

	public boolean isGameEnded() {
		return scoreBoard_.isEverybodyDone();
	}

	public String changeLettersOfPlayer(String playerName, String lettersToChange) {
		return letterManager_.changeLettersOfPlayer(playerName, lettersToChange);
	}

	///////////////////////////////////////////////////////////////
	///// Menu handling section
	///////////////////////////////////////////////////////////////
	
	public void checkAndApplyMyMove() {
		
		if (currentMove_.validate()) {
			applyCurrentMove();
			gameBoard_.startMyTurn(false);
		}
		else {
			gameBoard_.getHandler().post(new ToastRunnable(GameResources.getInstance().getResources().getString(R.string.move_forbidden), false));
			gameBoard_.cancelCurrentMove();
		}
	}

	public void skipTurn() {

		markMove(null);
		gameBoard_.cancelCurrentMove();
		if (amIServer())
			CommunicationManager.getInstance().sendMoveAndNextTurnMsgToEverybody(new MoveMessage(false));
		else {
			try {
				CommunicationManager.getInstance().writeAsClient(MsgSerializer.serialize(new MoveMessage(false)));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		gameBoard_.startMyTurn(false);
	}

	public void giveUp() {

		markMove(null);
		gameBoard_.cancelCurrentMove();
		if (amIServer()) {
			scoreBoard_.setPlayerDone(myName_);
			CommunicationManager.getInstance().sendMoveAndNextTurnMsgToEverybody(new MoveMessage(true));
		} else {
			try {
				CommunicationManager.getInstance().writeAsClient(MsgSerializer.serialize(new MoveMessage(true)));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		gameBoard_.startMyTurn(false);
		pauseGame(I_AM_DONE);
	}
	
	public void changeLetters() {

		gameBoard_.cancelCurrentMove();
		gameBoard_.showLetterChangePanel(true);
	}
	
	public void showScoreBoard() {

		final StringBuilder text = new StringBuilder();
		text.append("Tura: " + scoreBoard_.getCurrentPlayer() + "\nWyniki:\n");
		ArrayList<Pair<String, Integer>> results = new ArrayList<Pair<String,Integer>>();
		
		String playerName = null;
		for (int i = 0; i < scoreBoard_.getPlayerNames().size(); ++i) {
			playerName = scoreBoard_.getPlayerNames().get(i);
			results.add(new Pair<String, Integer>(playerName, scoreBoard_.getPlayerPoints(playerName)));
		}
		
		Collections.sort(results, new ScoreComparable());
		int i = 1;
		for (Pair<String, Integer> pos : results) {
			text.append(String.valueOf(i) + ". " + pos.first + "\t" + String.valueOf(pos.second) + "\n");
			++i;
		}
		
		gameBoard_.getHandler().post(new ToastRunnable(text.toString(), false));
	}
	
	///////////////////////////////////////////////////////////////
	///// Move handling
	///////////////////////////////////////////////////////////////
	
	private void applyCurrentMove() {

		markMove(null);
		gameBoard_.applyMove(currentMove_);
		firstTurn_ = false;
		if (amIServer()) {
			scoreBoard_.addPlayerPoints(myName_, currentMove_.getPoints());
			gameBoard_.acceptNewLetters(letterManager_.updatePlayerLetters(myName_, currentMove_.getLetters()));
			
			CommunicationManager.getInstance().sendMoveAndNextTurnMsgToEverybody(new MoveMessage(currentMove_));
		} else {
			try {
				CommunicationManager.getInstance().writeAsClient(MsgSerializer.serialize(new MoveMessage(currentMove_)));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		currentMove_.resetMove();
	}

	public void applyOuterMove(MoveInfo currentMove) {

		firstTurn_ = false;
		gameBoard_.applyMove(currentMove);
		markMove(currentMove);
		gameBoard_.postInvalidate();
	}

	public void cancelCurrentMove() {
		gameBoard_.cancelCurrentMove();
	}
	
	public void markMove(MoveInfo currentMove) {

		if (previousMove_ != null) {
			for (MovePart part : previousMove_.getLetters())
				gameBoard_.getSquare(part.getRow(), part.getCol()).markAsLastOuterMove(false);
		}
		previousMove_ = currentMove;
		if (currentMove != null) {
			for (MovePart part : currentMove.getLetters())
				gameBoard_.getSquare(part.getRow(), part.getCol()).markAsLastOuterMove(true);
		}
	}

	public void changeLetters(String letters) {
		
		gameBoard_.takeAwayLetters(letters);
		if (amIServer()) {
			gameBoard_.acceptNewLetters(letterManager_.changeLettersOfPlayer(myName_, letters));
			CommunicationManager.getInstance().sendMoveAndNextTurnMsgToEverybody(new MoveMessage(letters));
		} else {
			try {
				CommunicationManager.getInstance().writeAsClient(MsgSerializer.serialize(new MoveMessage(letters)));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public boolean amIServer() {
		return letterManager_ != null;
	}
	
	public void updateScoreBoard(ScoreBoard scoreBoard) {
		scoreBoard_ = scoreBoard;
	}

	public String getPlayerName() {
		return myName_;
	}
	
	public String getCurrentPlayer() {
		return scoreBoard_.getCurrentPlayer();
	}

	public void setStartMessage(StartMessage startMessage) {
		startMessage_ = startMessage;
	}
	
	public void applyGameData(GameData data) {
		
		if (gameBoard_ == null) {
			gameData_ = data;
			Intent gameBoardIntent = new Intent(CommunicationManager.getInstance().getCurrentActivity(), BoardActivity.class);
			CommunicationManager.getInstance().getCurrentActivity().startActivity(gameBoardIntent);
		} else {
			scoreBoard_ = data.getScoreBoard();
			gameBoard_.applyGameData(data);
		}
	}
	
	public void startTimer() {
		
		String time = GameResources.getInstance().getStringPreference(GameSettingsActivity.TURN_TIME);
		if (!time.equals("0")) {
			if (timer_ != null) {
				timer_.cancel();
				timer_.purge();
			}
			timer_ = new Timer();
			timer_.schedule(new TimerTask() {
				
				@Override
				public void run() {
					timeout();
				}
			}, Long.valueOf(time) * 60000);
		}
	}
	
	public void stopTimer() {
		
		if (timer_ != null) {
		   timer_.cancel();
		   timer_.purge();
		   timer_ = null;
		}
	}
	
	public void timeout() {
		
		if (gameBoard_ != null && isMyTurn())
			gameBoard_.cancelCurrentMove();
		nextTurn();
		CommunicationManager.getInstance().forwardMessage(new NewTurnMessage(scoreBoard_, false));
		startTimer();
	}
	
	public void turnNotifier() {

		vibrator_.vibrate(pattern, -1);
		gameBoard_.getHandler().post(new ToastRunnable((isMyTurn() ? GameResources.getInstance().getResources().getString(R.string.your_turn) : GameResources.getInstance().getResources().getString(R.string.turn_of_player, scoreBoard_.getCurrentPlayer())), true));
		gameBoard_.startMyTurn(isMyTurn());
	}
	
	public void moveNotifier(MoveMessage moveMsg) {

		Resources res = GameResources.getInstance().getResources();
		String senderName = playerManager_.getNameOfSender(moveMsg.getSenderDevice());
		if (moveMsg.getMoveInfo() != null)
			gameBoard_.getHandler().post(new ToastRunnable(res.getString(R.string.player_ended_turn_and_gained_pts, senderName, moveMsg.getMoveInfo().getPoints()), true));
		else if (moveMsg.getLettersToChange() != null)
			gameBoard_.getHandler().post(new ToastRunnable(res.getString(R.string.player_changed_letters, senderName), true));
		else if (moveMsg.isGiveUp())
			gameBoard_.getHandler().post(new ToastRunnable(res.getString(R.string.player_gave_up, senderName), true));
		else
			gameBoard_.getHandler().post(new ToastRunnable(res.getString(R.string.player_skipped_turn, senderName), true));
	}
	
	public void pauseGame(int reason) {
		
		//TODO: Pauza gry po zakończeniu aktualnej tury (o ile gra na czas), a nie hardkorowe przerywanie gry.
		if (amIServer())
			stopTimer();
		gamePaused_ = reason;
		gameBoard_.startMyTurn(false);
		switch (reason) {
		case PLAYERS_DONE:
			//TODO: string resource
			gameBoard_.getHandler().post(new ToastRunnable("Gra zakończona!", false));
			openOptionsMenu();
			break;
		case I_AM_DONE:
			//TODO: string resource
			gameBoard_.getHandler().post(new ToastRunnable("Zakończyłeś grę!", false));
			break;
		}
	}
	
	private void openOptionsMenu() {

		gameBoard_.getHandler().post(new Runnable() {
			
			public void run() {
				gameBoard_.getParentActivity().openOptionsMenu();
			}
		});
	}
	
	public void resumeGame() {
		
		gamePaused_ = RUNNING;
		if (amIServer()) {
			startTimer();
			CommunicationManager.getInstance().forwardMessage(new Message(Message.CONTINUE));
		}
		gameBoard_.startMyTurn(isMyTurn());
	}
	
	public boolean isGamePaused() {
		return gamePaused_ != RUNNING;
	}
	
	public void endGame() {

		ArrayList<String> players = scoreBoard_.getPlayerNames();
		ArrayList<Integer> points = scoreBoard_.getPoints();
		Intent resultsIntent = new Intent(gameBoard_.getContext(), GameResultsActivity.class);
		resultsIntent.putStringArrayListExtra("Players", players);
		resultsIntent.putIntegerArrayListExtra("Points", points);
		gameBoard_.getParentActivity().startActivity(resultsIntent);
		gameBoard_.finishBoardActivity();
		closeGame();
	}
	
	public void closeGame() {

		if (timer_ != null)
			stopTimer();
		letterManager_ = null;
		firstTurn_ = true;
		currentMove_ = null;
		scoreBoard_ = null;
		gameBoard_ = null;
		myName_ = null;
		startMessage_ = null;
		gameData_ = null;
	}

	public void nextTurn() {

		scoreBoard_.nextTurn();
		turnNotifier();
	}

	public void playerDisconnected(String name) {

		if (amIServer()) {
			gameBoard_.getHandler().post(new ToastRunnable(GameResources.getInstance().getResources().getString(R.string.player_left_the_game, name), true));
			CommunicationManager.getInstance().forwardMessage(new Message(Message.PAUSE));
		}
		else
			gameBoard_.getHandler().post(new ToastRunnable(GameResources.getInstance().getResources().getString(R.string.server_left_the_game), true));
		
		pauseGame(PLAYERS_DISCONNECTED);
	}

	public void playerReconnected(String name) {
		gameBoard_.getHandler().post(new ToastRunnable(GameResources.getInstance().getResources().getString(R.string.player_reconnected, name), true));
	}

	public void serverDisconnected() {
	
		pauseGame(SERVER_DISCONNECTED);
		CommunicationManager.getInstance().startReconnectTask();
	}
	
	public int getPauseReason() {
		return gamePaused_;
	}

	public String updatePlayersLetters(String name, ArrayList<MovePart> letters) {
		return letterManager_.updatePlayerLetters(name, letters);
	}

	public String getLettersOfPlayer(String name) {
		return letterManager_.getLettersOfPlayer(name);
	}

	public boolean canPlayerJoinGame(BluetoothDevice senderDevice) {
		return scoreBoard_.getPlayerNames().contains(senderDevice.getName());
	}
}
